﻿/* mvcForum
 * Copyright (C) 2010-2011 Steen F. Tøttrup
 * http://mvcforum.org/
 */

using System;
using System.Collections.Generic;
using System.Linq;
using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Specifications;
using mvcForum.Core.Interfaces.Services;

namespace mvcForum.DataProvider.EntityFramework.Repositories {

	public class PostRepository : IPostRepository {
		private readonly IRepository<Post> baseRepo;
		private readonly IForumAccessService accessService;
		private readonly IRepository<TopicTrack> ttRepo;

		public PostRepository(IRepository<Post> baseRepo, IForumAccessService accessService, IRepository<TopicTrack> ttRepo) {
			this.baseRepo = baseRepo;
			this.accessService = accessService;
			this.ttRepo = ttRepo;
		}

		/// <summary>
		/// Read all posts of a topic.
		/// </summary>
		/// <param name="user">The active user if any.</param>
		/// <param name="topic">The topic in question.</param>
		/// <param name="showDeleted">Should deleted posts be included in the result.</param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException">If no topic was given.</exception>
		public IEnumerable<Post> ReadAll(ForumUser user, Topic topic, Boolean showDeleted) {
			if (topic == null) {
				throw new ArgumentNullException("topic");
			}
			if (this.accessService.HasAccess(topic.Forum, user, AccessFlag.Read)) {
				return this.baseRepo.ReadMany(new PostSpecifications.RegularListing(topic, showDeleted));
			}
			return new Post[] { };
		}

		/// <summary>
		/// Read the posts visible on the given page for the given topic.
		/// </summary>
		/// <param name="user">The active user if any.</param>
		/// <param name="topic">The topic in question.</param>
		/// <param name="pageIndex">The page to be shown</param>
		/// <param name="postsPerPage">Posts per page.</param>
		/// <param name="showDeleted">Should deleted posts be included in the result.</param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException">If no topic was given.</exception>
		/// <exception cref="ArgumentException">If page is less then 1.</exception>
		public IEnumerable<Post> Read(ForumUser user, Topic topic, Int32 pageIndex, Int32 postsPerPage, Boolean showDeleted) {
			if (topic == null) {
				throw new ArgumentNullException("topic");
			}
			if (pageIndex < 1) {
				throw new ArgumentException("page");
			}
			if (this.accessService.HasAccess(topic.Forum, user, AccessFlag.Read)) {
				return this.baseRepo.ReadMany(new PostSpecifications.RegularListing(topic, showDeleted))
							.Skip((pageIndex - 1) * postsPerPage)
							.Take(postsPerPage)
							.ToList();
			}
			return new Post[] { };
		}

		/// <summary>
		/// Locates the page the user last viewed, and gets the posts for that page on the given topic.
		/// </summary>
		/// <param name="user">The active user if any.</param>
		/// <param name="topic">The topic in question.</param>
		/// <param name="postsPerPage">Posts per page.</param>
		/// <param name="showDeleted">Should deleted posts be included in the result.</param>
		/// <returns></returns>
		/// <exception cref="ArgumentNullException">If no topic or user was given.</exception>
		public IEnumerable<Post> ReadSinceLast(ForumUser user, Topic topic, Int32 postsPerPage, Boolean showDeleted, out DateTime? lastRead, out Int32 showingPage) {
			lastRead = DateTime.MinValue;
			showingPage = 1;
			if (topic == null) {
				throw new ArgumentNullException("topic");
			}
			if (user == null) {
				throw new ArgumentNullException("user");
			}
			if (this.accessService.HasAccess(topic.Forum, user, AccessFlag.Read)) {
				// Any topic track on this topic by the given user?
				TopicTrack tt = this.ttRepo.ReadOne(new TopicTrackSpecifications.SpecificTopicAndUser(topic, user));
				Int32 skipPages = 0;
				if (tt != null) {
					// TopicTrack found, let's find the page the user last viewed!
					DateTime topicLastViewed = tt.LastViewed.ToUniversalTime();
					lastRead = topicLastViewed;
					// Get the number of posts the user has already seen (-1 for the topic post).
					Int32 olderPosts = this.baseRepo.ReadMany(new PostSpecifications.RegularListing(topic, showDeleted)).Where(p => p.Posted <= topicLastViewed).Count() - 1;

					// Any "older" posts?
					if (olderPosts > 0) {
						// Let's calculated the last page the user viewed ( +1 for the topic post).
						skipPages = (Int32)Math.Floor(((Decimal)olderPosts + 1) / (Decimal)postsPerPage);
						showingPage = skipPages + 1;
					}
				}

				// Get the posts from the last viewed page.
				return this.Read(user, topic, skipPages + 1, postsPerPage, showDeleted);
			}
			return new Post[] { };
		}
	}
}